package com.foreverwin.jzymes.framework.websocket;


import com.alibaba.fastjson.JSONObject;
import com.foreverwin.jzymes.framework.util.SpringUtils;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketHandler;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.web.context.support.GenericWebApplicationContext;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class WebSocketServer extends WebSocketHandler {

    private static Logger logger = LoggerFactory.getLogger( WebSocketServer.class );

    /**
     * Active web sockets set
     */
    private static final ConcurrentHashMap<String, ArrayList<WebSocket.Connection>> connectMap = new ConcurrentHashMap<>();

    private static final ConcurrentHashMap<String, Class<? extends TextMessageWebSocket>> websocketHandleMap = new ConcurrentHashMap<>();

    /**
     * Executing on new connetcion establishment
     *
     * @param request
     * @param protocol
     *            protocol (it can be ws and wss)
     * @return
     */
    @Override
    public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {

        String channel = request.getParameter( "channel" );
        TextMessageWebSocket webSocket = null;
        try{
            if( websocketHandleMap.containsKey( channel ) ){
                webSocket = websocketHandleMap.get( channel ).newInstance();
                webSocket.setChannel( channel );
                injectDependency( webSocket );
                /*if( webSocket.getEvents() != null && webSocket.getEvents().size() > 0 ){
                    ApplicationContext applicationContext = SpringUtils.getApplicationContext();
                    if( applicationContext instanceof GenericWebApplicationContext){
                        GenericWebApplicationContext webApplicationContext = (GenericWebApplicationContext) applicationContext;
                        webApplicationContext.addApplicationListener( webSocket );
                        ConfigurableListableBeanFactory beanFactory = webApplicationContext.getBeanFactory();
                        ApplicationEventMulticaster applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
                        applicationEventMulticaster.removeApplicationListener( webSocket );
                    }
                }*/
            }
        } catch ( Exception e ) {
            e.printStackTrace();
        }

        return webSocket;
    }

    /**
     * 向通道发送消息
     *
     * @param channel
     * @param message
     */
    public static void sendMessageToChannel( String channel, String message ){
        if( channel == null ) return;
        ArrayList<WebSocket.Connection> webSockets = connectMap.get( channel );
        try{
            if( webSockets != null ){
                for (WebSocket.Connection webSocket : webSockets) {
                    webSocket.sendMessage( message );
                }
            }
        }catch (IOException e) {
            logger.info( "推送消息：" + message + "，到页面：" + channel + "，失败：" + e.getMessage() );
        }
    }

    public static void sendMessageToChannel(String channel, String messageType, Object messageBody ){
        if( channel == null ) return;
        ArrayList<WebSocket.Connection> webSockets = connectMap.get( channel );
        try{
            if( webSockets != null ){
                JSONObject message = new JSONObject();
                message.put( "MODE", "PS" );
                message.put( "MESSAGE_TYPE", messageType );
                message.put( "CODE", 0 );
                message.put( "ERROR_MSG", "" );
                message.put( "MESSAGE", messageBody );
                for (WebSocket.Connection webSocket : webSockets) {
                    webSocket.sendMessage( message.toString() );
                }
            }
        }catch (IOException e) {
            logger.info( "推送消息：" + messageBody.toString() + "，到页面：" + channel + "，失败：" + e.getMessage() );
        }
    }

    public static void registerWebSocket( String channel, WebSocket.Connection connection ){

        ArrayList<WebSocket.Connection> webSockets;
        if( connectMap.containsKey( channel ) ){
            webSockets = connectMap.get( channel );
        }else{
            webSockets = new ArrayList();
        }
        webSockets.add( connection );
        connectMap.put( channel, webSockets );
    }

    public static void removeWebSocket( String channel, WebSocket.Connection connection ){

        ArrayList<WebSocket.Connection> webSockets = connectMap.get( channel );
        if( webSockets != null ){
            webSockets.remove( connection );
        }

    }

    private void injectDependency( TextMessageWebSocket webSocket ){
        try{
            Set<Field> fieldList = ReflectionUtils.getAllFields( webSocket.getClass() );
            for( Field field : fieldList ){
                Autowired aw = field.getAnnotation( Autowired.class );
                if( aw != null ){
                    field.setAccessible( true );
                    Object fieldObj = SpringUtils.getBean( field.getType() );
                    field.set( webSocket, fieldObj );
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    static {
        Reflections reflections = new Reflections("com.foreverwin.jzymes");
        Set<Class<? extends TextMessageWebSocket>> classList =  reflections.getSubTypesOf(TextMessageWebSocket.class);
        for( Class clazz : classList ){
            if( !Modifier.isAbstract( clazz.getModifiers() ) ){
                com.foreverwin.jzymes.framework.annotation.WebSocketHandler handler = (com.foreverwin.jzymes.framework.annotation.WebSocketHandler) clazz.getAnnotation( com.foreverwin.jzymes.framework.annotation.WebSocketHandler.class );
                if( handler != null && handler.channel() != null ){
                    websocketHandleMap.put( handler.channel(), clazz );
                }
            }
        }
    }

}
